package org.acghub.mtdb.core;

import org.acghub.mtdb.common.exception.BeanManagedError;
import org.acghub.mtdb.common.exception.LockLackedError;

import java.util.concurrent.atomic.AtomicLong;

import static org.acghub.mtdb.core.Transaction.LockeyHolderType.READ;
import static org.acghub.mtdb.core.Transaction.LockeyHolderType.WRITE;

/**
 * @author zhouhe
 */
public abstract class TBean implements Marshal {
    private static final AtomicLong OBJ_ID = new AtomicLong();
    private static final Runnable NOOP = () -> {
    };
    private final Long _objid_ = OBJ_ID.incrementAndGet();
    private TBean _parent_;
    private String _varname_;

    protected TBean(TBean parent, String varname) {
        _parent_ = parent;
        _varname_ = varname;
        this.onConstructor();
    }

    public Long getObjId() {
        return _objid_;
    }

    public TBean getParent() {
        return _parent_;
    }

    public String getVarname() {
        return _varname_;
    }

    final void link(TBean parent, String varname, boolean log) {
        if (null != parent) {
            if (null != _parent_) {
                // parent != null && _parent_ != null
                throw new BeanManagedError("ambiguously managed");
            }
            if (parent == this) {
                throw new BeanManagedError("loop managed");
            }
        } else if (null == _parent_) {
            // parent == null && _parent_ == null
            throw new BeanManagedError("not managed");
        }
        if (log) {
            Transaction.currentSavepoint().addIfAbsent(new LogKey(this, "_parent_"), new Log() {
                private final TBean parent = _parent_;
                private final String varname = _varname_;

                @Override
                public void commit() {
                }

                @Override
                public void rollback() {
                    _parent_ = parent;
                    _varname_ = varname;
                }
            });
        }
        _parent_ = parent;
        _varname_ = varname;
    }

    public void notify(LogNotify notify) {
        if (_parent_ != null) {
            _parent_.notify(notify.push(new LogKey(_parent_, _varname_)));
        }
    }

    private TRecord<?, ?> getRecord() {
        TBean self = this;
        do {
            if (self instanceof TRecord<?, ?>) {
                return (TRecord<?, ?>) self;
            }
            self = self._parent_;
        } while (self != null);
        return null;
    }

    protected final Runnable _verify_unsafe_(String methodName, boolean readonly) {
        if (!Mtdb.INSTANCE.config().isVerifyUnsafe()) {
            return NOOP;
        }
        Transaction current = Transaction.current();
        if (current == null) {
            return NOOP;
        }
        TRecord<?, ?> record = getRecord();
        if (record == null) {
            return NOOP;
        }
        Transaction.LockeyHolderType type = current.getLockeyHolderType(record.getLockey());
        if (type == WRITE) {
            return NOOP;
        } else if (type == READ && readonly) {
            //获得的读锁，并且也只是get字段，普通字段没问题，对于容器类型的字段，但是你小子不讲武德要修改
            return () -> {
                throw new LockLackedError(getClass().getName() + "." + methodName);
            };
        }
        throw new LockLackedError(getClass().getName() + "." + methodName);
    }

    /**
     * 构造函数默认值初始化
     */
    protected abstract void onConstructor();

    @Override
    public abstract int hashCode();

    @Override
    public abstract boolean equals(Object obj);

    @Override
    public abstract String toString();
}
